package eval

import (
	"showen/obj"
	"testing"
)

func TestStringToList(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with normal string
	result := StringToList([]obj.Object{&obj.StringObject{Value: "abc"}}, env)
	if result.Type() != obj.PAIR_OBJ {
		t.Errorf("Expected PAIR_OBJ, got %v", result.Type())
	}

	// Check that we have a list of 3 characters
	pair := result.(*obj.PairObject)
	if pair.Car.(*obj.CharacterObject).Value != 'a' {
		t.Errorf("Expected 'a', got %v", pair.Car)
	}

	// Check second character
	secondPair := pair.Cdr.(*obj.PairObject)
	if secondPair.Car.(*obj.CharacterObject).Value != 'b' {
		t.Errorf("Expected 'b', got %v", secondPair.Car)
	}

	// Check third character
	thirdPair := secondPair.Cdr.(*obj.PairObject)
	if thirdPair.Car.(*obj.CharacterObject).Value != 'c' {
		t.Errorf("Expected 'c', got %v", thirdPair.Car)
	}

	// Check that list ends properly
	if thirdPair.Cdr != obj.EMPTY_LIST {
		t.Errorf("Expected EMPTY_LIST, got %v", thirdPair.Cdr)
	}

	// Test with empty string
	result = StringToList([]obj.Object{&obj.StringObject{Value: ""}}, env)
	if result != obj.EMPTY_LIST {
		t.Errorf("Expected EMPTY_LIST for empty string, got %v", result)
	}

	// Test with wrong number of arguments
	result = StringToList([]obj.Object{}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}

	// Test with wrong argument type
	result = StringToList([]obj.Object{&obj.NumberObject{}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}
}

func TestListToString(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with normal list of characters
	list := &obj.PairObject{
		Car: &obj.CharacterObject{Value: 'a'},
		Cdr: &obj.PairObject{
			Car: &obj.CharacterObject{Value: 'b'},
			Cdr: &obj.PairObject{
				Car: &obj.CharacterObject{Value: 'c'},
				Cdr: obj.EMPTY_LIST,
			},
		},
	}

	result := ListToString([]obj.Object{list}, env)
	if result.Type() != obj.STRING_OBJ {
		t.Errorf("Expected STRING_OBJ, got %v", result.Type())
	}

	str := result.(*obj.StringObject)
	if str.Value != "abc" {
		t.Errorf("Expected \"abc\", got \"%v\"", str.Value)
	}

	// Test with empty list
	result = ListToString([]obj.Object{obj.EMPTY_LIST}, env)
	if result.Type() != obj.STRING_OBJ {
		t.Errorf("Expected STRING_OBJ, got %v", result.Type())
	}

	str = result.(*obj.StringObject)
	if str.Value != "" {
		t.Errorf("Expected empty string, got \"%v\"", str.Value)
	}

	// Test with wrong number of arguments
	result = ListToString([]obj.Object{}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}

	// Test with wrong argument type
	result = ListToString([]obj.Object{&obj.NumberObject{}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}

	// Test with list containing non-characters
	badList := &obj.PairObject{
		Car: &obj.CharacterObject{Value: 'a'},
		Cdr: &obj.PairObject{
			Car: &obj.NumberObject{},
			Cdr: obj.EMPTY_LIST,
		},
	}

	result = ListToString([]obj.Object{badList}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}
}

func TestStringCopy(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with normal string
	result := StringCopy([]obj.Object{&obj.StringObject{Value: "hello"}}, env)
	if result.Type() != obj.STRING_OBJ {
		t.Errorf("Expected STRING_OBJ, got %v", result.Type())
	}

	str := result.(*obj.StringObject)
	if str.Value != "hello" {
		t.Errorf("Expected \"hello\", got \"%v\"", str.Value)
	}

	// Test with wrong number of arguments
	result = StringCopy([]obj.Object{}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}

	// Test with wrong argument type
	result = StringCopy([]obj.Object{&obj.NumberObject{}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}
}

func TestStringFill(t *testing.T) {
	env := obj.NewEnvironment()

	// Test with normal arguments
	strObj := &obj.StringObject{Value: "hello"}
	result := StringFill([]obj.Object{strObj, &obj.CharacterObject{Value: 'x'}}, env)
	
	// Check return value (should be NULL_OBJECT)
	if result != obj.NULL_OBJECT {
		t.Errorf("Expected NULL_OBJECT, got %v", result)
	}

	// Check that string was modified
	if strObj.Value != "xxxxx" {
		t.Errorf("Expected \"xxxxx\", got \"%v\"", strObj.Value)
	}

	// Test with wrong number of arguments
	result = StringFill([]obj.Object{}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}

	// Test with wrong first argument type
	result = StringFill([]obj.Object{&obj.NumberObject{}, &obj.CharacterObject{Value: 'x'}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}

	// Test with wrong second argument type
	result = StringFill([]obj.Object{&obj.StringObject{Value: "hello"}, &obj.NumberObject{}}, env)
	if result.Type() != obj.ERROR_OBJ {
		t.Errorf("Expected ERROR_OBJ, got %v", result.Type())
	}
}